#include "vanila/dsobject.h"
#include "vanila/baseobject.h"
#include "vanila/allocator.h"
#include <vector>
#include <iostream>

namespace vanila
{
static Allocator* allocator = utils::Singleton<Allocator>::instance();

ObjectList::ObjectList():
    Object{ObjectType::LIST}, _list{}
{}

ObjectList::ObjectList(std::vector<Value> list):
    Object{ObjectType::LIST}, _list{std::move(list)}
{}

//! \brief print object list
//! \param[in] callStr wheather call '__str__' method to print
void ObjectList::print(bool callStr) const
{
    static_cast<void>(callStr);
    std::cout << '[';
    for (size_t i = 0; i < this->_list.size(); ++i)
    {
        if (i != 0)
            std::cout << ", ";
        this->_list[i].print();
    }
    std::cout << ']';
}

//! \brief wheather this == that
bool ObjectList::equal(const Object* that) const
{
    const ObjectList* other = that->asList();
    if (this->_list.size() != other->_list.size())
        return false;
    
    for (size_t i = 0; i < this->_list.size(); ++i)
    {
        if (!this->_list[i].equal(other->_list[i]))
            return false;
    }

    return true;
}

//! \brief add two object 
Value ObjectList::add(const Object* that) const
{
    size_t size = this->_list.size() + that->asList()->_list.size();
    std::vector<Value> list(size, Value{});

    size_t i = 0;
    for (i = 0; i < this->_list.size(); ++i)
        list[i] = this->_list[i];

    size_t j = 0;
    for (j = 0; j < that->asList()->_list.size(); ++j)
        list[i + j] = that->asList()->_list[j];
    
    ObjectInstance* instance = allocator->allocateInstance(ObjectClass::list);
    ObjectList* listObject = allocator->allocateList(std::move(list));
    instance->setObject(listObject);
    return Value{instance};
}

//! \brief insert a value to specify index
//! \param[in] index specify index
//! \param[in] value value
//! \return if insert successfully, return true
bool ObjectList::insert(size_t index, const Value& value)
{ 
    if (index > this->_list.size())
        return false;

    this->_list.insert(this->_list.begin() + index, value);
    return true;
}

//! \brief earse a value in specify index
//! \param[in] index specify index
//! \return if pop successfully, return true
bool ObjectList::pop(size_t index) noexcept
{ 
    if (index >= this->_list.size())
        return false;
    
    this->_list.erase(this->_list.begin() + index); 
    return true;
}

//! \brief get the value by specify index
//! \param[in] index the specify index
//! \param[out] value value reference
//! \return true if index is illage
bool ObjectList::get(int64_t index, Value& value) const noexcept
{
    int64_t size = this->_list.size();

    if (index < 0 && (index += size) < 0)
        return false;
    else if (index >= size)
        return false;
    
    value = this->_list.at(index);
    return true;
}

}